package com.intct;

import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;

/**
 * @author gufg
 * @create 2024-09-23 16:55
 */
public class IntctHBaseDDL {
    //  声明一个静态属性
    public static Connection connection = IntctHBaseConnection.connection;

    /**
     * 创建命名空间
     *
     * @param namespace 命名空间名称
     */
    public static void createNamespace(String namespace) throws IOException {
        // 1.获取admin对象
        // 此处的异常先不要抛出 等待方法写完 再统一进行处理
        // Admin的连接是轻量级的 不是线程安全的 不推荐池化或者缓存连接
        Admin admin = connection.getAdmin();

        // 2. 创建命名空间
        // 与hbase shell对比 结论：代码相对shell更加底层 所以shell能实现的功能 代码肯定能实现
        // 所以HBase让大家填写对命名空间更加详细的描述


        // 备注： new NamespaceDescriptor()进源码查看，构造都是私有化，不能直接创建
        // 备注：采用构造器时，需要写多个，比较麻烦。如有建造者模式，给使用者更大的发挥空间
        // 2.1创建命名空间描述建造者 ==> 设计师
        NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespace);

        // 2.2 给命名空间添加键值
        builder.addConfiguration("user", "intct");

        // 2.3 创建命名空间
        // 备注：创建命名空间出现的问题 都属于本方法自身的问题 不应该抛出 直接捕获
        try {
            admin.createNamespace(builder.build());
        } catch (IOException e) {
            System.out.println("命名空间已经存在，请确认后重试！");
            e.printStackTrace();
        } finally {
            if (admin != null) {
                // 3. 关闭 admin
                admin.close();
            }
        }
    }

    /**
     * 判断表格是否存在
     *
     * @param namespace 命名空间名称
     * @param tableName 表格名称
     * @return ture 表示存在
     */
    public static boolean isTableExists(String namespace, String tableName) throws IOException {
        // 1. 获取 admin
        Admin admin = connection.getAdmin();

        // 2. 使用方法判断表格是否存在
        boolean b = false;
        try {
            b = admin.tableExists(TableName.valueOf(namespace, tableName));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 关闭 admin
        admin.close();

        // 3. 返回结果
        return b;
        // 后面的代码不能生效
    }

    /**
     * 创建表
     *
     * @param namespace      命名空间名称
     * @param tableName      表格名称
     * @param columnFamilies 列族名称 可以有多个
     */
    public static void createTable(String namespace, String tableName, String... columnFamilies) throws IOException {
        // 判断是否有至少一个列族
        if (columnFamilies.length == 0) {
            System.out.println("创建表格至少有一个列族");
            return;
        }
        // 判断表格是否存在
        if (isTableExists(namespace, tableName)) {
            System.out.println("表格已经存在");
            return;
        }
        // 1.获取 admin
        Admin admin = connection.getAdmin();
        // 2. 调用方法创建表格
        // 2.1 创建表格描述的建造者
        TableDescriptorBuilder tableDescriptorBuilder =
                TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace,
                        tableName));
        // 2.2 添加参数
        for (String columnFamily : columnFamilies) {
            // 2.3 创建列族描述的建造者
            ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));
            // 2.4 对应当前的列族添加参数
            // 添加版本参数
            columnFamilyDescriptorBuilder.setMaxVersions(5);
            // 2.5 创建添加完参数的列族描述

            tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptorBuilder.build());
        }

        // 7.创建时对应region的预分区
        byte[][] splits = new byte[3][];
        splits[0] = Bytes.toBytes("1001");
        splits[1] = Bytes.toBytes("1002");
        splits[2] = Bytes.toBytes("1003");
        splits[3] = Bytes.toBytes("1004");

        // 2.6 创建对应的表格描述
        try {
            admin.createTable(tableDescriptorBuilder.build(), splits);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (admin != null) {
                // 3. 关闭 admin
                admin.close();
            }
        }
    }

    /**
     * 修改表中一个列族的版本
     *
     * @param namespace    命名空间名称
     * @param tableName    表格名称
     * @param columnFamily 列族名称
     * @param version      版本
     */

    // 1、 先在hbase shell查看列族是否存在
    // 2、 执行代码测试
    // 3、 查看报错：列族不存在
    // 4、 主要原因是我们在使用的是创建表，采用了不在的存在的信息，说明tableDescriptorBuilder不存在之前不存的信息
//    public static void modifyTable1(String namespace, String tableName, String columnFamily, int version) throws IOException {
//        // 1. 获取 admin
//        Admin admin = connection.getAdmin();
//
//        // 2.1 创建一个表格描述建造者
//        TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace, tableName));
//
//        // 2.2 对应建造者进行表格数据的修改
//        ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));
//
//        // 修改对应的版本
//        columnFamilyDescriptorBuilder.setMaxVersions(version);
//
//        // 此处修改的时候
//        tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorBuilder.build());
//
//        try {
//            admin.modifyTable(tableDescriptorBuilder.build());
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//
//        admin.close();
//    }

    public static void modifyTable(String namespace, String tableName, String columnFamily, int version) throws IOException {
        // 判断表格是否存在
        if (!isTableExists(namespace, tableName)) {
            System.out.println("表格不存在无法修改");
            return;
        }
        // 1. 获取 admin
        Admin admin = connection.getAdmin();
        try {
            // 2. 调用方法修改表格
            // 2.0 获取之前的表格描述
            TableDescriptor descriptor = admin.getDescriptor(TableName.valueOf(namespace, tableName));
            // 2.1 创建一个表格描述建造者
            // 如果使用填写 tableName 的方法 相当于创建了一个新的表格描述建造者 没有之前的信息
            // 如果想要修改之前的信息 必须调用方法填写一个旧的表格描述
            TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(descriptor);
            // 2.2 对应建造者进行表格数据的修改
            ColumnFamilyDescriptor columnFamily1 = descriptor.getColumnFamily(Bytes.toBytes(columnFamily));
            // 创建列族描述建造者
            // 需要填写旧的列族描述
            ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);
            // 修改对应的版本
            columnFamilyDescriptorBuilder.setMaxVersions(version);

            // 此处修改的时候 如果填写的新创建 那么别的参数会初始化
            tableDescriptorBuilder.modifyColumnFamily(columnFamilyDescriptorBuilder.build());
            admin.modifyTable(tableDescriptorBuilder.build());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (admin != null) {
                // 3. 关闭 admin
                admin.close();
            }
        }
    }

    /**
     * 删除表格
     *
     * @param namespace 命名空间名称
     * @param tableName 表格名称
     * @return true 表示删除成功
     */
    public static boolean deleteTable(String namespace, String tableName) throws IOException {
        // 1. 判断表格是否存在
        if (!isTableExists(namespace, tableName)) {
            System.out.println("表格不存在 无法删除");
            return false;
        }
        // 2. 获取 admin
        Admin admin = connection.getAdmin();
        // 3. 调用相关的方法删除表格
        try {
            // HBase 删除表格之前 一定要先标记表格为不可以
            TableName tableName1 = TableName.valueOf(namespace, tableName);
            admin.disableTable(tableName1);
            admin.deleteTable(tableName1);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 4. 关闭 admin
        admin.close();
        return true;
    }

    /**
     * 测试入口
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        // 创建命名空间测试
        // 注：应该先保证连接没有问题 再来调用相关的方法
//        createNamespace("intct");

        // 创建表
//        createTable("intct", "users", "info", "msg");

        // 修改表
//        modifyTable("intct", "users", "info", 6);

        // 删除表
        deleteTable("intct", "users");
        // 其他代码
        System.out.println("输出正常，开心吧");

        // 关闭连接
        IntctHBaseConnection.closeConnection();
    }
}
